StockPriceChart.tsx 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. 'use client';
  2. import './price-chart.scss';
  3. import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
  4. import type { StockPriceRow } from '@/types/stock';
  5. import { changeDirection, formatNumber } from '@/lib/utils/stock';
  6. // 종목 상세 시세 그래프 — 최근 종가 시계열(과거→최신) 영역 차트. stock-detail__basis 바로 하단.
  7. // 색은 래퍼 dir 클래스(.stock-chart--up/down/flat)의 CSS color 를 currentColor 로 상속
  8. // (recharts stroke/fill 은 SVG presentation attribute 라 var() 미해석 → currentColor 사용).
  9. type Props = {
  10. // GetDetail.recentPrices — 최신순(newest-first)
  11. prices: StockPriceRow[];
  12. name: string;
  13. };
  14. export default function StockPriceChart({ prices, name }: Props)
  15. {
  16. // 데이터 2개 미만이면 미표시 (하단 일별 시세 표로 대체)
  17. if (!prices || prices.length < 2) {
  18. return null;
  19. }
  20. // 과거→최신 순서로 뒤집어 종가 시계열 구성
  21. const series = [...prices].reverse().map(p => ({ date: p.tradingDate, close: p.close }));
  22. const dir = changeDirection(series[series.length - 1].close - series[0].close);
  23. return (
  24. <figure className={`stock-chart stock-chart--${dir}`}>
  25. <figcaption className='stock-chart__caption'>최근 {series.length}일 종가 추이</figcaption>
  26. <div className='stock-chart__canvas' aria-label={`${name} 최근 ${series.length}일 종가 추이 그래프`} role='img'>
  27. <ResponsiveContainer width='100%' height='100%'>
  28. <AreaChart data={series} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>
  29. <XAxis dataKey='date' hide />
  30. <YAxis hide domain={['dataMin', 'dataMax']} />
  31. <Tooltip
  32. cursor={{ stroke: 'currentColor', strokeOpacity: 0.3 }}
  33. contentStyle={{
  34. background: 'var(--bg-elevated)',
  35. border: '1px solid var(--border-default)',
  36. borderRadius: 0,
  37. fontSize: 'var(--fs-xs)',
  38. color: 'var(--text-primary)'
  39. }}
  40. labelStyle={{ color: 'var(--text-muted)' }}
  41. formatter={(value) => {
  42. const n = Array.isArray(value) ? Number(value[0]) : Number(value);
  43. return [formatNumber(Number.isFinite(n) ? n : null), '종가'];
  44. }}
  45. labelFormatter={(label) => String(label)}
  46. />
  47. <Area
  48. type='monotone'
  49. dataKey='close'
  50. stroke='currentColor'
  51. strokeWidth={2}
  52. fill='currentColor'
  53. fillOpacity={0.1}
  54. dot={false}
  55. isAnimationActive={false}
  56. />
  57. </AreaChart>
  58. </ResponsiveContainer>
  59. </div>
  60. </figure>
  61. );
  62. }